package mirrormonkey.util.annotations.hfilter;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import mirrormonkey.util.annotations.hfilter.ClassFilter.HierarchyType;
import mirrormonkey.util.annotations.hfilter.ClassFilter.SelectType;

import org.junit.Test;

/**
 * Contains test cases for mirrormonkey.util.annotations.hfilter.ClassFilter.
 * 
 * @author Philipp Christian Loewner
 * 
 */
public class ClassFilterTest {

	/**
	 * Convenience annotation to specify an array of class filters
	 */
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface ClassFilterCollection {
		public ClassFilter[] value();
	}

	/**
	 * Checks whether an empty array works together with SelectType.INCLUDE
	 */
	@Test
	@ClassFilterCollection(@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.INCLUDE, classes = {}))
	public void testIncludeAll() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testIncludeAll", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertTrue(ClassFilter.Eval.contains(filters, ClassFilterTest.class));
	}

	/**
	 * Checks whether an empty array works together with SelectType.EXCLUDE
	 */
	@Test
	@ClassFilterCollection(@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.EXCLUDE, classes = {}))
	public void testExcludeAll() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testExcludeAll", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertFalse(ClassFilter.Eval.contains(filters, ClassFilterTest.class));
	}

	/**
	 * Some class extending <tt>ClassFilterTest</tt> to test inheritance
	 */
	public static class ExtendsClassFilterTest extends ClassFilterTest {
	}

	/**
	 * Checks whether a specific class (and only that class) can be included via
	 * <tt>HierarchyType.SPECIFIC</tt>
	 */
	@Test
	@ClassFilterCollection({
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.EXCLUDE, classes = {}),
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.INCLUDE, classes = { ClassFilterTest.class }) })
	public void testIncludeSpecific() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testIncludeSpecific", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertTrue(ClassFilter.Eval.contains(filters, ClassFilterTest.class));
		assertFalse(ClassFilter.Eval.contains(filters,
				ExtendsClassFilterTest.class));
	}

	/**
	 * Checks whether a specific class (and only that class) can be excluded via
	 * <tt>HierarchyType.SPECIFIC</tt>
	 */
	@Test
	@ClassFilterCollection({
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.INCLUDE, classes = {}),
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.EXCLUDE, classes = { ClassFilterTest.class }) })
	public void testExcludeSpecific() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testExcludeSpecific", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertFalse(ClassFilter.Eval.contains(filters, ClassFilterTest.class));
		assertTrue(ClassFilter.Eval.contains(filters,
				ExtendsClassFilterTest.class));
	}

	/**
	 * Checks whether <tt>HierarchyType.HIERARCHY</tt> applies an inclusion to
	 * every subclass
	 */
	@Test
	@ClassFilterCollection({
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.EXCLUDE, classes = {}),
			@ClassFilter(hierarchy = HierarchyType.HIERARCHY, select = SelectType.INCLUDE, classes = { ClassFilterTest.class }) })
	public void testIncludeSubclass() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testIncludeSubclass", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertTrue(ClassFilter.Eval.contains(filters,
				ExtendsClassFilterTest.class));
	}

	public static interface Interfaze {
	}

	public static final class Clazz implements Interfaze {
	}

	/**
	 * Checks whether <tt>HierarchyType.HIERARCHY</tt> applies an inclusion to
	 * every class inheriting a given interface
	 */
	@Test
	@ClassFilterCollection({
			@ClassFilter(hierarchy = HierarchyType.SPECIFIC, select = SelectType.EXCLUDE, classes = {}),
			@ClassFilter(hierarchy = HierarchyType.HIERARCHY, select = SelectType.INCLUDE, classes = { Interfaze.class }) })
	public void testIncludeInterface() throws Throwable {
		ClassFilter[] filters = getClass()
				.getMethod("testIncludeInterface", new Class<?>[0])
				.getAnnotation(ClassFilterCollection.class).value();
		assertTrue(ClassFilter.Eval.contains(filters, Clazz.class));
	}

}
